/*******************************************************************************
 * File name: 		uart.c
 * Description: 
 * Project: 		uart
 * Target: 			LPC2478
 * Compiler: 		arm-none-eabi-gcc
 * Date: 			05-06-2012
 * Author: 			Kuba
 * Based on: 		---
 ******************************************************************************/

/*==============================================================================
 Includes
==============================================================================*/
# include <string.h>
# include "LPC24xx.h"
# include "hdr_scb.h"
# include "typedef.h"
# include "init.h"

# include "uart.h"

/*==============================================================================
 Defines
==============================================================================*/
# define UART_PORTS              4
# define UART_ISR_ARR            16
# define UART_INTID_MASK         0xE

# define UART0_PINSEL_MASK       0xF0
# define UART2_PINSEL_MASK       0xF00000

# define UART0_BASE_DIV          16
# define UART1_BASE_DIV          16
# define UART2_BASE_DIV          16
# define UART3_BASE_DIV          16

# define LUT_BAUD_SIZE           22
# define LUT_WORD_LEN_SIZE       4
# define LUT_TRIG_LVL_SIZE       4
/*==============================================================================
 Types
==============================================================================*/
enum uart {
   uart0 = 0,
   uart1,
   uart2,
   uart3
};
/* private struct to save addresses for ISRs */
struct uartPrivate {
   uint8 *txBuff;                               /* IRQ TX buffer */
   uint8 *rxBuff;                               /* IRQ RX buffer */
   uint32 txDataSize;                           /* size */
   uint32 rxDataSize;
   enum uartRxIrqLvl rxIrqLvl;                  /* RX IRQ trigger level */
   void (*txIrqService) (void);                 /* user interrupt services called at the end of transfers */
   void (*rxIrqService) (void);
   void (*timeoutService)(void);
};

struct lut {
   uint32 baud[LUT_BAUD_SIZE];
   uint32 wordLen[LUT_WORD_LEN_SIZE];
   uint32 triggerLvl[LUT_TRIG_LVL_SIZE];
   //uint32 triggerVal[LUT_TRIG_LVL_SIZE];        /* 1, 4, 8, 14 */
};
/*==============================================================================
 Globals
==============================================================================*/
/* array of uarts 0-3 */
/* array contains private vars to pass data between program and ISRs */
static struct uartPrivate privateUart[UART_PORTS];
/* look up tables */
static struct lut lut = {
      .baud = {50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, 9600,
            19200, 38400, 57600, 64000, 115200, 125000, 230400, 460800, 576000},
      .wordLen = {U_LCR_WORD_LENGTH_SELECT_5BIT, U_LCR_WORD_LENGTH_SELECT_6BIT,
            U_LCR_WORD_LENGTH_SELECT_7BIT, U_LCR_WORD_LENGTH_SELECT_8BIT},
      .triggerLvl = {U_FCR_RX_FIFO_TRIGGER_LEVEL_1CHAR, U_FCR_RX_FIFO_TRIGGER_LEVEL_4CHAR,
            U_FCR_RX_FIFO_TRIGGER_LEVEL_8CHAR, U_FCR_RX_FIFO_TRIGGER_LEVEL_14CHAR},
      //.triggerVal = {1, 4, 8, 14}
};
/* array to keep data from an interrupt */
static uint8 arr[UART_ISR_ARR];
/*==============================================================================
 Static ISR prototypes
==============================================================================*/
//static void uart0Isr (void) __attribute__((interrupt("IRQ")));
static void uart2Isr (void) __attribute__((interrupt("IRQ")));

/*==============================================================================
 Static function prototypes
==============================================================================*/
static void setService(enum uart uart, const struct uartConf *uartConf);
static void dummyService (void);

/*==============================================================================
 Global function definitions
==============================================================================*/
/*------------------------------------------------------------------------------
 function name:   uart0Init
 description:     init UART 0
 parameters:      pointer to configuration struct
 returned value:  none
------------------------------------------------------------------------------*/


/*------------------------------------------------------------------------------
 function name:   uart1Init
 description:     init UART 1
 parameters:      pointer to configuration struct
 returned value:  none
------------------------------------------------------------------------------*/


/*------------------------------------------------------------------------------
 function name:   uart2Init
 description:     init UART 2
 parameters:      pointer to configuration struct
 returned value:  true or false
------------------------------------------------------------------------------*/
bool uart2Init (const struct uartConf *uartConf)
{
   uint32 fDiv;

   setService(uart2, uartConf);
   /* switch on UART 2 */
   PCONP |= PCONP_PCUART2;
   /* set DLAB to enable access to divisor latches- DLL and DLM are accessible,
    * 8 bit character, 1 bit stop, none parity */
   U2LCR = (lut.wordLen[uartConf->wordLen]) | (uartConf->stopBit) | (uartConf->parity)| U_LCR_DLAB;
   /* set divisor latches DLL, DLM */
   fDiv = (FREQUENCY / UART2_BASE_DIV) / lut.baud[uartConf->baud];
   U2DLM = fDiv / 256;
   U2DLL = fDiv % 256;
   /* clear DLAB to disable DLL, DLM- now RBR, THR and IER are accessible */
   U2LCR &= ~U_LCR_DLAB;

   /* clear all interrupts */
   U2IER = 0;
//   if (uartConf->txIrq == uartTX_INTERRUPT_ENABLE)
//   {
//      U2IER = U_IER_INTERRUPT_EN_THRE;
//   }

   if (uartConf->rxIrq == uartRX_IRQ_ENABLE)
   {
      switch(uartConf->rxIrqLvl)
      {
      case uartRX_IRQ_TRIGGER_LEVEL_1:
         /* enable & reset fifo- it must be enabled for correct UART working */
         U2FCR = U_FCR_FIFO_ENABLE | U_FCR_FIFO_RST_RX | U_FCR_FIFO_RST_TX | U_FCR_RX_FIFO_TRIGGER_LEVEL_1CHAR;
         privateUart[uart2].rxIrqLvl = uartRX_IRQ_TRIGGER_LEVEL_1;
         break;

      case uartRX_IRQ_TRIGGER_LEVEL_4:
         U2FCR = U_FCR_FIFO_ENABLE | U_FCR_FIFO_RST_RX | U_FCR_FIFO_RST_TX | U_FCR_RX_FIFO_TRIGGER_LEVEL_4CHAR;
         privateUart[uart2].rxIrqLvl = uartRX_IRQ_TRIGGER_LEVEL_4;
         break;

      case uartRX_IRQ_TRIGGER_LEVEL_8:
         U2FCR = U_FCR_FIFO_ENABLE | U_FCR_FIFO_RST_RX | U_FCR_FIFO_RST_TX | U_FCR_RX_FIFO_TRIGGER_LEVEL_8CHAR;
         privateUart[uart2].rxIrqLvl = uartRX_IRQ_TRIGGER_LEVEL_8;
         break;

      case uartRX_IRQ_TRIGGER_LEVEL_14:
         U2FCR = U_FCR_FIFO_ENABLE | U_FCR_FIFO_RST_RX | U_FCR_FIFO_RST_TX | U_FCR_RX_FIFO_TRIGGER_LEVEL_14CHAR;
         privateUart[uart2].rxIrqLvl = uartRX_IRQ_TRIGGER_LEVEL_14;
         break;

      default:
         U2FCR = U_FCR_FIFO_ENABLE | U_FCR_FIFO_RST_RX | U_FCR_FIFO_RST_TX | U_FCR_RX_FIFO_TRIGGER_LEVEL_1CHAR;
         break;
      }
   }
   else
   {
      /* no interrupts- reset fifo */
      U2FCR = U_FCR_FIFO_ENABLE | U_FCR_FIFO_RST_RX | U_FCR_FIFO_RST_TX;
   }
   /* set UART 2 pins */
   PINSEL0 &= ~UART2_PINSEL_MASK;
   PINSEL0 |= PINSEL0_TXD2 | PINSEL0_RXD2;

   /* interrupts */
   if ((uartConf->txIrq == uartTX_IRQ_ENABLE) || (uartConf->rxIrq == uartRX_IRQ_ENABLE))
   {
      if (irqInit(irqUART2, (void *) uart2Isr, irqLOWEST_PRIORITY) == FALSE)
      {
         return (FALSE);
      }
   }

   return TRUE;
}

/*------------------------------------------------------------------------------
 function name:   uart2IrqEnable
 description:     enable UART 2 TX interrupts
 parameters:      pointer to TX buffer, size
 returned value:  none
------------------------------------------------------------------------------*/
void uart2IrqTxEnable (const uint8 *txBuff, const uint32 size)
{
//   if (uartConf->txIrqService == 0)    /* check if user service is 0 */
//   {
//      privateUart[uart2].txInterruptService = dummyService;
//   }
//   else
//   {
//      privateUart[uart2].txInterruptService = uartIrqConf->txIrqService;
//   }
//   privateUart[uart2].txBuff = txBuff;
//   privateUart[uart2].txDataSize = size;

   U2IER = U_IER_INTERRUPT_EN_THRE;             /* enable TX interrupt */
}

/*------------------------------------------------------------------------------
 function name:   uart2IrqEnable
 description:     enable UART 2 RX interrupts
 parameters:      pointer to RX buffer, size, pointer to configuration struct
 returned value:  none
------------------------------------------------------------------------------*/
void uart2IrqRxEnable(uint8 *rxBuff, const uint32 size)
{
   privateUart[uart2].rxBuff = rxBuff;
   privateUart[uart2].rxDataSize = size;

   /* enable RX interrupt */
   U2IER |= U_IER_INTERRUPT_EN_RBR;
}

/*------------------------------------------------------------------------------
 function name:   uart2Tx
 description:     send data
 parameters:      pointer to TX buffer, size
 returned value:  none
------------------------------------------------------------------------------*/
void uart2Tx (const uint8 *txBuff, const uint32 size)
{
   uint32 i;

   for (i = 0; i < size; ++i)
   {
      while (!(U2LSR & U_LSR_THR_EMPTY));
      U2THR = *txBuff++;
   }
}

/*------------------------------------------------------------------------------
 function name:   uart2Rx
 description:     receive data
 parameters:      pointer to RX buffer, size
 returned value:  none
------------------------------------------------------------------------------*/
void uart2Rx (uint8 *rxBuff, const uint32 size)
{
   uint32 i;

   for (i = 0; i < size; ++i)
   {
      while (!(U2LSR & U_LSR_RECEIVER_DATA_READY));
      *rxBuff++ = U2RBR;
   }
}

/*------------------------------------------------------------------------------
 function name:   uart2PutChar
 description:     send a character
 parameters:      character to send
 returned value:  none
------------------------------------------------------------------------------*/
void uart2PutChar (const char character)
{
   while (!(U2LSR & U_LSR_THR_EMPTY));
   U2THR = character;
}

/*------------------------------------------------------------------------------
 function name:   uart2GetChar
 description:     get a character from the UART register
 parameters:      none
 returned value:  received character
------------------------------------------------------------------------------*/
uint8 uart2GetChar (void)
{
   while (!(U2LSR & U_LSR_RECEIVER_DATA_READY));
   return (U2RBR);
}

/*==============================================================================
 Static function definitions
==============================================================================*/
/*------------------------------------------------------------------------------
 function name:   setService
 description:     set function pointers
 parameters:      uart number, pointer to const configuration struct
 returned value:  none
------------------------------------------------------------------------------*/
static void setService(enum uart uart, const struct uartConf *uartConf)
{

   if (uartConf->rxIrqService == 0)    /* check if user service is 0 */
   {
      privateUart[uart].rxIrqService = dummyService;
   }
   else
   {
      privateUart[uart].rxIrqService = uartConf->rxIrqService;
   }
   if (uartConf->timeoutService == 0)
   {
      privateUart[uart].timeoutService = dummyService;
   }
   else
   {
      privateUart[uart].timeoutService = uartConf->timeoutService;
   }
}

/*------------------------------------------------------------------------------
 function name:   dummyService
 description:     does nothing
 parameters:      none
 returned value:  none
------------------------------------------------------------------------------*/
static void dummyService (void)
{
   return;
}

/*==============================================================================
 Static ISR definitions
==============================================================================*/
/*------------------------------------------------------------------------------
 function name:   uart0Isr
 description:     UART0 ISR
 parameters:      none
 returned value:  none
------------------------------------------------------------------------------*/
//static void uart0Isr(void)
//{
//
//}

/*------------------------------------------------------------------------------
 function name:   uart2Isr
 description:     UART2 ISR
 parameters:      none
 returned value:  none
------------------------------------------------------------------------------*/
static void uart2Isr(void)
{
   volatile uint32 status;
   static uint32 cnt = 0;

   status = U2IIR;

   if ((status & UART_INTID_MASK) == U_IIR_INTERRUPT_ID_THRE)  /* transmitter empty */
   {
      (*privateUart[uart2].txIrqService)();                    /* call user service */
   }

   if ((status & UART_INTID_MASK) == U_IIR_INTERRUPT_ID_RDA)   /* receive data available */
   {
      switch (privateUart[uart2].rxIrqLvl)
      {
      case uartRX_IRQ_TRIGGER_LEVEL_1:
         if (cnt >= privateUart[uart2].rxDataSize) /* if counter exceeds user buffer */
         {
            cnt = 0;
            (*privateUart[uart2].rxIrqService)();  /* call user service */
         }
         privateUart[uart2].rxBuff[cnt++] = U2RBR; /* get char and clear interrupt */
         break;

      case uartRX_IRQ_TRIGGER_LEVEL_4:
         if (cnt > privateUart[2].rxDataSize - 4)  /* if user gives 13 chars 12 will be saved here and the remaining in CTI */
         {
            cnt = 0;
            (*privateUart[uart2].rxIrqService)();  /* call user service */
         }
         arr[0] = U2RBR;                           /* get chars and clear interrupt */
         arr[1] = U2RBR;
         arr[2] = U2RBR;
         arr[3] = U2RBR;
         memcpy(privateUart[uart2].rxBuff + cnt, arr, 4);  /* copy chars to user buffer */
         cnt += 4;
         break;

      case uartRX_IRQ_TRIGGER_LEVEL_8:
         if (cnt > privateUart[uart2].rxDataSize - 8)
         {
            cnt = 0;
            (*privateUart[uart2].rxIrqService)();  /* call user service */
         }
         arr[0] = U2RBR;
         arr[1] = U2RBR;
         arr[2] = U2RBR;
         arr[3] = U2RBR;
         arr[4] = U2RBR;
         arr[5] = U2RBR;
         arr[6] = U2RBR;
         arr[7] = U2RBR;
         memcpy(privateUart[uart2].rxBuff + cnt, arr, 8);
         cnt += 8;
         break;

      case uartRX_IRQ_TRIGGER_LEVEL_14:
         if (cnt > privateUart[uart2].rxDataSize - 14)
         {
            cnt = 0;
            (*privateUart[uart2].rxIrqService)();  /* call user service */
         }
         arr[0] = U2RBR;
         arr[1] = U2RBR;
         arr[2] = U2RBR;
         arr[3] = U2RBR;
         arr[4] = U2RBR;
         arr[5] = U2RBR;
         arr[6] = U2RBR;
         arr[7] = U2RBR;
         arr[8] = U2RBR;
         arr[9] = U2RBR;
         arr[10] = U2RBR;
         arr[11] = U2RBR;
         arr[12] = U2RBR;
         arr[13] = U2RBR;
         memcpy(privateUart[uart2].rxBuff + cnt, arr, 14);
         cnt += 14;
         break;

      default:
         break;
      }
   }
   if ((status & UART_INTID_MASK) == U_IIR_INTERRUPT_ID_CTI)   /* timeout */
   {
      if (cnt >= privateUart[uart2].rxDataSize)                /* if counter exceeds user buffer */
      {
         cnt = 0;
      }
      privateUart[uart2].rxBuff[cnt++] = U2RBR;                /* get char and clear interrupt */
      (*privateUart[uart2].timeoutService)();                  /* call timeout service every time */
   }

   VICVectAddr = 0;
}
/******************************************************************************
* END OF FILE
******************************************************************************/



